

                     L                ZZZZZZ         RRRRR           SSSSS
                     L                    Z          R    R         S
                     L          aaa      Z      aaa  R    R  u   u  S
                     L            a     Z         a  RRRRR   u   u  SSSSS
               XX    L         aaaa    Z       aaaa  R    R  u   u       S
              XXXX   L        a   a   Z       a   a  R    R  u   u       S
             XXXXXX  LLLLLLL  aaaaa  ZZZZZZZ  aaaaa  R    R  uuuuu  SSSSSS
             XXXXXX       
        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
             XXXXXX
             XXXXXX
              XXXX        proudly presents his 32.Cracking Tutorial (18.12.1999)
               XX               My solution of the 3nd Kraecker mAG Project

I.   Introduction
I.1  The tools
II.  The essay
II.1 The CrackMe
II.2 DocClear
III. BTW

I.   Introduction
     I participated (again) at the Cracking project of the German Scene EZine Kraecker. The 
     targets to crack were FireWorx' CrackMe 16 and the shareware app DocClear 2.0.0.1 - 
     The better you crack them, the more points you achieve. I have to explain the solutions
     shortly, but as I don't like short solutions, I can write a tut about it.

I.1  The tools
     For the CrackMe:
       SoftIce (I have v3.25)
       MASM (to compile the KeyGen)
     For DocClear:
       SoftIce
       MASM (to compile the KeyGen)
     "Intellectual" support: The CD "Krebskolonie" from Eisregen

II.  The essay
II.1 The CrackMe
     When you start this CrackMe you see that you gotta enter a name, a company and a serial.
     Hit OK and you see that there is no MessageBox that says "Wrong serial" but nothing
     happens instead. So, the decision for a breakpoint is easy: hmemcpy

     Enter name/company/serial and set a "bpx hmemcpy" in SICE. Leave SICE and hit the OK
     button. When SICE breaks enter "bc *" to clear all breakpoints and hit F12 until you
     reach the line 441742. Here is a listing of the important part: Note that this listing is
     created using a memory dump and then disassembled, so the offsets of the Instructions and
     the calls/jumps will differ:

     The first line is 441737
     :00000000 8B45D4                  mov eax, dword ptr [ebp-2C] ;; EAX = NAME
     :00000003 E8EA22FCFF              call FFFC22F2 ;; THIS CALL GETS THE LENGTH OF THE NAME
     :00000008 83E802                  sub eax, 00000002 ;; SUB 2 FROM LENGTH
     :0000000B 0F8C02010000            jl 00000113 ;; IF 0 OR BELOW: JUMP
     :00000011 40                      inc eax
     :00000012 8945DC                  mov dword ptr [ebp-24], eax ;; USELESS SAVING OF LENGTH
     :00000015 C745F002000000          mov [ebp-10], 00000002 ;; MOVES 2 TO DWORD PTR [EBP-10]
     :0000001C 8B45F0                  mov eax, dword ptr [ebp-10] ;; EAX = 2
     :0000001F F76DF0                  imul [ebp-10] ;; EAX = EAX * EAX = 2*2 = 4
     :00000022 83C038                  add eax, 00000038 ;; EAX = 3Ch
     :00000025 3534640000              xor eax, 00006434 ;; EAX = 6408
     :0000002A 8945EC                  mov dword ptr [ebp-14], eax ;; SAVE THIS VALUE
     :0000002D 8B7DEC                  mov edi, dword ptr [ebp-14] ;; EDI = 6408
     :00000030 0FAF7DEC                imul edi, dword ptr [ebp-14] ;; EDI = EDI * 6408 =27164040
     :00000034 81C749030000            add edi, 00000349 ;; EDI = 27164389
     :0000003A 81F772020000            xor edi, 00000272 ;; EDI = 271641FB
     :00000040 8D55D4                  lea edx, dword ptr [ebp-2C]
     :00000043 8B45FC                  mov eax, dword ptr [ebp-04]
     :00000046 8B80C8020000            mov eax, dword ptr [eax+000002C8]
     :0000004C E8111AFEFF              call FFFE1A62
     :00000051 8B45D4                  mov eax, dword ptr [ebp-2C] ;; EAX POINTS TO SERIAL
     :00000054 E89922FCFF              call FFFC22F2 ;; THIS ONE GETS LENGTH OF NAME
     :00000059 83E802                  sub eax, 00000002 ;; SAME CHECK
     :0000005C 0F8CA5000000            jl 00000107 ;; AS ABOVE
     :00000062 40                      inc eax
     :00000063 8945D8                  mov dword ptr [ebp-28], eax ;; USELESS
     :00000066 BB02000000              mov ebx, 00000002 ;; EBX = 2
     :0000006B 8BC3                    mov eax, ebx ;; EAX = 2
     :0000006D F7EB                    imul ebx ;; EAX = EAX * EBX = 2*2 = 4
     :0000006F B938240000              mov ecx, 00002438 ;; ECX = 2438h
     :00000074 99                      cdq ;; NEEDED FOR DIVISION
     :00000075 F7F9                    idiv ecx ;; EAX = EAX / ECX = 0 ; EDX = EAX MOD ECX =2438
     :00000077 8BC8                    mov ecx, eax ;; ECX = 0
     :00000079 81F173640000            xor ecx, 00006473 ;; ECX = 6473h
     :0000007F 83E918                  sub ecx, 00000018 ;; ECX = 645B
     :00000082 8D3419                  lea esi, dword ptr [ecx+ebx] ;; ESI=ECX+EBX=645B+2 = 645D
     :00000085 8BC7                    mov eax, edi ;; EAX = 271641FB
     :00000087 F76DEC                  imul [ebp-14] ;; EAX=EAX*[ebp-14]=645D*271641FB=EA781BD8
     :0000008A 03F0                    add esi, eax ;; ESI = ESI + EAX = EA788035
     :0000008C 8BC7                    mov eax, edi ;; EAX = EA788035
     :0000008E 99                      cdq ;; NEEDED FOR DISIVSION
     :0000008F F77DEC                  idiv [ebp-14] ;; EAX=EAX/[ebp-14] = 6408
     :00000092 8BD6                    mov edx, esi ;; EDX = EA788035
     :00000094 0FAFD1                  imul edx, ecx ;; EDX = EDX * ECX = 6AEA46D7
     :00000097 03C2                    add eax, edx ;; EAX = 6AEAAADF
     :00000099 8945E8                  mov dword ptr [ebp-18], eax
     :0000009C 8B45E8                  mov eax, dword ptr [ebp-18]
     :0000009F 99                      cdq
     :000000A0 F7FE                    idiv esi ;; EAX = EAX / ESI = FFFFFFFC
     :000000A2 03C7                    add eax, edi ;; EAX = 271641F7
     :000000A4 05B40C0000              add eax, 00000CB4 ;; EAX = 27164EAB
     :000000A9 8945E4                  mov dword ptr [ebp-1C], eax
     :000000AC C745E0FFE0F505          mov [ebp-20], 05F5E0FF
     :000000B3 03F1                    add esi, ecx ;; ESI = EA78E490
     :000000B5 0375E8                  add esi, dword ptr [ebp-18] ;; ESI = 55638F6F
     :000000B8 8B45E4                  mov eax, dword ptr [ebp-1C] ;; EAX DOESN'T CHANGE HERE
     :000000BB F76DE0                  imul [ebp-20] ;; EAX = EAX * [ebp-20] = 14B4FC55
     :000000BE 03F0                    add esi, eax ;; ESI = 6A188BC4
     ------------- HERE HAS ESI THE CORRECT SERIAL - CONVERT TO DEC AND YOU GET 1779993540
     ------------- THE FOLLOWING CALLS ARE ONLY FOR CONVERTING AND COMPARING THE SERIALS
     :000000C0 8D55F8                  lea edx, dword ptr [ebp-08]
     :000000C3 8BC6                    mov eax, esi
     :000000C5 E8245EFCFF              call FFFC5EEE

     As you see the serial does NOT depend on name or company but is always 1779993540.
     Actually there is no need to keygen this one, but 1) FireWorx says to do it in his NFO
     2) You can - perhaps - learn something from the plenty sources I included and 3)
     I get points at the Kraecker project ;)

     Now we can code a loader for this CrackMe, so that it shows always the "Good Cracker"
     message. If you trace farther below, you will find this check pretty fast:

     :000000E6 8B45D4                  mov eax, dword ptr [ebp-2C]
     :000000E9 8B55F4                  mov edx, dword ptr [ebp-0C]
     :000000EC E81123FCFF              call FFFC2402
     :000000F1 750A                    jne 000000FD ;; THIS IS OFFSET 441833

     And exactly this is the "Wrong serial" jump. Now we gotta make it *never* jump. Some of
     you might say: "Let's NOP it", some other "INC EAX, DEC EAX", but that's all shit ;) The
     best way to make a jump never jump is to make it jump 0 Bytes. So we gotta change 750A
     to 7500. This is - IMHO - much better style and we need only 50% of the bytes we would
     have to patch using NOP or INC/DEC ;) The source for a loader is included in the ZIP, too.
     btw: This patch makes the "Good Cracker" message appear as often as the company you entered
     is long - 1.

II.2 DocClear
     Ok, now the next one and to say it right now: This one was quite more difficult than the
     Crackme, but nevertheless a good target ;)
     At first - as always - the keygen ;)
     The helpfile of the program says that there are *no* limitations in the program when it
     is unregistered and in fact: If there were one or two limits the program would have no
     function left ;) 10$ for a prog that clears your start menu? Never!
     Ok, when you start the program it gets started as a icon in your system tray. Here you can
     choose a menu which makes the register-window appear. When you enter a wrong serial, a
     messagebox appears telling you that you entered a wrong serial. Neither bpx on MessageBox
     nor MessageBoxA work but if you disassemble DocClear.exe with W32Dasm you can find the
     string "Incorrect or incomplete information was entered". You will find it here:

     :0046862D 8D95FCFDFFFF            lea edx, dword ptr [ebp+FFFFFDFC]
     :00468633 8B83CC020000            mov eax, dword ptr [ebx+000002CC]
     :00468639 59                      pop ecx
     :0046863A E84959FEFF              call 0044DF88
     :0046863F 85C0                    test eax, eax
     :00468641 7529                    jne 0046866C
     :00468643 8B83CC020000            mov eax, dword ptr [ebx+000002CC]
     :00468649 E80A5AFEFF              call 0044E058
     :0046864E 6A00                    push 00000000
     :00468650 668B0DC0864600          mov cx, word ptr [004686C0]
     :00468657 B202                    mov dl, 02

     * Possible StringData Ref from Code Obj ->"DocClear was registered successfully."
                                       |
     :00468659 B8CC864600              mov eax, 004686CC
     :0046865E E8CDFDFDFF              call 00448430
     :00468663 8BC3                    mov eax, ebx
     :00468665 E87A92FDFF              call 004418E4
     :0046866A EB23                    jmp 0046868F

     * Referenced by a (U)nconditional or (C)onditional Jump at Address:
     |:00468641(C)
     |
     :0046866C 6A00                    push 00000000
     :0046866E 668B0DC0864600          mov cx, word ptr [004686C0]
     :00468675 B202                    mov dl, 02

     * Possible StringData Ref from Code Obj ->"Incorrect or incomplete information "
                                             ->"was entered."
                                       |
     :00468677 B8FC864600              mov eax, 004686FC
     :0046867C E8AFFDFDFF              call 00448430

     OK, let's analyze: The jump at :00468641 decides whether the "Correct" or the "Incorrect"
     messagebox appears. In front of it there is a call and some pushes. At first glance I'd
     have bet 10$ that the correct serial is one of the pushes, but I was wrong. This is just
     the beginning of the calculation and comparison routines and your name and serial you 
     entered are pushed. When you enter a correct serial, the first call after the "DocClear
     was..." string shows the messagebox and the second one writes - as we will see later - the
     registration information to a file. I thought that this call had this function because
     what else should a call do that only appears when you entered the correct serial? If
     you enter a wrong serial, the "Incorrect..." message appears and the program just goes on.

     Prepare for much tracing into calls as this prog tries to hide the important routines deep
     in the "dark codewoods" ;)

     So, trace in the call and you find yourself at :0044DF88. Now trace on until you reach the
     following section (Remark: In serial calculations, try to find loops. They are mostly 
     important)

     :0044DFBF 8A54240A                mov dl, byte ptr [esp+0A] ;; DL = LENGTH OF NAME
     :0044DFC3 42                      inc edx ;; EDX = LENGTH OF NAME + 1
     :0044DFC4 83FA32                  cmp edx, 00000032 ;; COMPARE EDX WITH 32h
     :0044DFC7 7F0E                    jg 0044DFD7 ;; IF EDX IS GREATER, THEN JUMP
     :0044DFC9 8D44140A                lea eax, dword ptr [esp+edx+0A] ;; EAX POINTS TO FIRST
                                                                       ;; BYTE AFTER THE NAME
     * Referenced by a (U)nconditional or (C)onditional Jump at Address:
     |:0044DFD5(C)
     |
     :0044DFCD C60019                  mov byte ptr [eax], 19 ;; THIS LOOP FILLS
     :0044DFD0 42                      inc edx                ;; A REGION
     :0044DFD1 40                      inc eax                ;; OF 32h BYTES (INCLUDING YOUR
     :0044DFD2 83FA33                  cmp edx, 00000033      ;; NAME) WITH 19h
     :0044DFD5 75F6                    jne 0044DFCD           ;; BYTES
     
     Now trace on until you reach the next call. It is at :0044DFDF.
     Enter and trace a little until you find this (only few bytes away from the start of the 
     call):

     :0044DEED 8D442440                lea eax, dword ptr [esp+40]
     :0044DEF1 50                      push eax
     :0044DEF2 8BCB                    mov ecx, ebx
     :0044DEF4 8D54240E                lea edx, dword ptr [esp+0E] ;; EDX = NAME
     :0044DEF8 8BC6                    mov eax, esi
     :0044DEFA E805FEFFFF              call 0044DD04
     :0044DEFF 8D442440                lea eax, dword ptr [esp+40] ;; EAX POINTS TO A SUSPICIOUS
                                                                   ;; STRING
     :0044DF03 8BD4                    mov edx, esp                ;; EDX = SERIAL YOU ENTERED

     Well, to say it directly: The string eax points to is the correct serial for the name you
     have entered. For example for "LaZaRuS" it will be "$716B5842". Now we gotta trace into
     the call at :0044DEFA to see how the serial gets calculated. 
 
     When you trace a while you will come to another loop which just adds 19h bytes to your name
     until you have a 32h bytes long segment of name and 19h bytes. This loop is from :0044DD3C
     to :0044DD44. Now the interesting part begins:

     * Referenced by a (U)nconditional or (C)onditional Jump at Address:
     |:0044DD36(C)
     |
     :0044DD46 8D8D6CFFFFFF            lea ecx, dword ptr [ebp+FFFFFF6C]
     :0044DD4C 8B9324020000            mov edx, dword ptr [ebx+00000224] ;; EDX = 9BFD62F25
     :0044DD52 8BC3                    mov eax, ebx
     :0044DD54 E853FEFFFF              call 0044DBAC ;; THIS CALL CONVERTS EDX TO "$9BFD62F25"
     :0044DD59 8D956CFFFFFF            lea edx, dword ptr [ebp+FFFFFF6C] ;; HERE EDX POINTS TO IT

     This call converts the integer value of edx to a string and moves it in front of the name
     in the memory. A byte which contains the length of the following string is in front of
     every string DocClear uses. So the memory part we deal with looks like this (bytes = []): 

     [9 = Length of "$9BFD62F25"]$9BFD62F25[7=Length of LaZaRuS (Name)]LaZaRuS[19][19]...

     The following calls and instructions do nothing but move strings like "$9BFD62F25" or your
     name to different memory adresses. The next important call is at

     :0044DD94 8D8D6CFFFFFF            lea ecx, dword ptr [ebp+FFFFFF6C]
     :0044DD9A 8B9328020000            mov edx, dword ptr [ebx+00000228] ;; EDX = E40FB826
     :0044DDA0 8BC3                    mov eax, ebx
     :0044DDA2 E805FEFFFF              call 0044DBAC

     This call converts the integer value of edx to a string and moves it at the end of the
     FirstString/Name/19hBytes block. The block now looks like this:

     [9]$9BFD62F25[7]LaZaRuS[19][19]...[9]$E40FB826
     You gotta think of so many 19h Bytes that the following equation is correct:
     3 Bytes for the length of the three strings + 18 Bytes for the two $-Strings + Name + 
     X 19h Bytes = 47h

     And again: The next operations do only move strings in the memory. But now we get to the
     core of the algo that calculates the serial: Look at this part:

     :0044DDE8 8B9334030000            mov edx, dword ptr [ebx+00000334]
     :0044DDEE 8D8D7BFFFFFF            lea ecx, dword ptr [ebp+FFFFFF7B]
     :0044DDF4 8BC3                    mov eax, ebx
     :0044DDF6 E869FDFFFF              call 0044DB64
 
     Look what edx contains after we trace *over* the call: 716B5842 (or more general: The 
     correct serial w/o $). Well, I strongly believe that this call is the end of our journey
     through the code :) Enter it and you will see the following:

     * Referenced by a CALL at Addresses:
     |:0044DDF6   , :0044DE5B   , :0044DEA0   
     |
     :0044DB64 53                      push ebx
     :0044DB65 56                      push esi
     :0044DB66 57                      push edi
     :0044DB67 83C4B8                  add esp, FFFFFFB8
     :0044DB6A 8BF1                    mov esi, ecx ;; ESI POINTS TO "$9BFD62F25"
     :0044DB6C 8D3C24                  lea edi, dword ptr [esp]
     :0044DB6F B911000000              mov ecx, 00000011
     :0044DB74 F3                      repz
     :0044DB75 A5                      movsd
     :0044DB76 66A5                    movsw
     :0044DB78 A4                      movsb
     :0044DB79 B147                    mov cl, 47 ;; CL = 47 = LOOP VARIABLE
     :0044DB7B 8BC4                    mov eax, esp ;; EAX POINTS TO "$9BFD62F25"

     * Referenced by a (U)nconditional or (C)onditional Jump at Address:
     |:0044DBA1(C) ;; THIS LOOP CALCULATES THE SERIAL :)
     |
     :0044DB7D 8BDA                    mov ebx, edx ;; EBX = EDX
     :0044DB7F C1EB08                  shr ebx, 08 ;; EBX = EBX * 2^8 = EBX * 256
     :0044DB82 81E3FFFFFF00            and ebx, 00FFFFFF ;; EBX = EBX AND 00FFFFFF
     :0044DB88 0FB630                  movzx esi, byte ptr [eax] ;; LOOK BELOW (1)
     :0044DB8B 33D6                    xor edx, esi ;; EDX = EDX XOR ESI
     :0044DB8D 81E2FF000000            and edx, 000000FF ;; EDX=CL (all higher bytes are erased)
     :0044DB93 8B1495B4AB4600          mov edx, dword ptr [4*edx+0046ABB4] ;;GET DWORD FROM TABLE
     :0044DB9A 33DA                    xor ebx, edx ;; EBX = EBX + EDX
     :0044DB9C 8BD3                    mov edx, ebx ;; EDX = EBX
     :0044DB9E 40                      inc eax
     :0044DB9F FEC9                    dec cl
     :0044DBA1 75DA                    jne 0044DB7D

     :0044DBA3 8BC2                    mov eax, edx ;; EAX CONTAINS CORRECT SERIAL
     :0044DBA5 83C448                  add esp, 00000048
     :0044DBA8 5F                      pop edi
     :0044DBA9 5E                      pop esi
     :0044DBAA 5B                      pop ebx
     :0044DBAB C3                      ret

     (1) Here, ESI gets the i. byte of the [9]$9BFD62F25[7]LaZaRuS[19][19]...[9]$E40FB826 block.
     This loop is run until every byte of this 47h block was in esi once. 

     :0044DB8D 81E2FF000000            and edx, 000000FF ;; EDX=CL (all higher bytes are erased)
     :0044DB93 8B1495B4AB4600          mov edx, dword ptr [4*edx+0046ABB4] ;;GET DWORD FROM TABLE

     Those two lines get a value from a "table". That is a block of bytes that is used to
     calculate serials. The length of the table is easy to get: In line :0044DB8D you see that
     edx can get a max value of FFh (at least if you know how the AND instruction works ;)
     I tell you just this: AND a byte with 00 and it will be 00; AND a byte with FFh and its
     value will remain.
     In the next line you see that the dword is received from [4*edx+0046ABB4]. 0046ABB4 is
     prolly the beginning of the table and 4*edx is the position of the dword which is read.
     So we calculate 4*FF = 3FCh - This could be the length of the table, but you have to watch
     out that you add 4 Bytes which will be read if edx has the value FFh. So, the length of the
     table is 3FCh+4h = 400h = 1024 = 1KB

     The table is easy to get, too. Just trace through the serial calculation and look for the
     first 8 bytes at :0046ABB4. Search for them in a hexeditor and copy/paste the following
     1024 Bytes in a new file. You will find the file table.hex in the keygen sourcecode.


     So, let's head for the patch. I believe the following part of the code is the best for a
     patch:

     :0044DEFF 8D442440                lea eax, dword ptr [esp+40] ;; EAX POINTS TO A SUSPICIOUS
                                                                   ;; STRING
     :0044DF03 8BD4                    mov edx, esp                ;; EDX = SERIAL YOU ENTERED

     I hope you remember this section ;)

     The best way to patch this one is to change it the following way:
     :0044DEFF 8D442440                lea eax, dword ptr [esp+40] ;; EAX = CORRECT SERIAL
     :0044DF03 8BD0                    mov edx, eax                ;; EDX = CORRECT SERIAL

     I just gave the register that should point to the serial you entered the value of the serial
     that was calculated. So the prog believes that you entered the correct serial and you get
     the "Correct Serial" message and the name you entered appears in the About-Box. And best
     of it all: This will stay so when you restart the program, as this section is used at the 
     beginning, too ;)

III. BTW

Credits go to:
Shadow - for the skeleton of the TASM source
CrackZ - for the HexToChar routine I used for the DOS-ASM source
NaTzGUL - for the essay where I have 90% of my loader source from

Greetings go to: +Sandman, Acid Burn, alpine, Blind Angel, Borna Janes,
Carpathia, CrazyKnight, DEATH, DEZM, dimwitz, DnNuke, duelist, Eternal Bliss,
Fravia+, Iczelion, Jordan, KnowledgeIsPower, Knotty, Lucifer48, MisterE,
Neural Noise, noos, Prof.X, R!SC, rubor, Shadow, SiG, tC, The AntiXryst, The
Hobgoblin, TORN@DO, ultraschall, viny, Volatility, wAj, _y and all the guys
I forget and I'll add next time.